package yasuo;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.zip.GZIPOutputStream;

import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.tools.tar.TarOutputStream;

/**
 * 压缩文件成tar.gz格式  
 */
public class TarUtils {
	private static int BUFFER = 1024 * 4; // 缓冲大小
	private static byte[] B_ARRAY = new byte[BUFFER];

	/**
	 * 生成tar.gz文件
	 * @param inputFileName 要打包的文件夹或文件的路径(严重警告：inputFileName不能是targetFileName的祖先路径)
	 * @param targetFileName 打包后的文件路径
	 * @param base 压缩文件的路径
	 * @return
	 */
	public static File createTarGz(String inputFileName, String targetFileName, String base) {
		File inputFile = new File(inputFileName);
		if(!inputFile.exists())
			return null;
		TarArchiveOutputStream out = getTarOutputStream(targetFileName);
		File tarFile = new File(targetFileName);
		tarPack(out, inputFile, base, tarFile);
		try {
			if (null != out) {
				out.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		File tarGzFile = compress(tarFile);
		tarFile.delete();
		return tarGzFile;
	}

	/**
	 * 生成tar.gz文件
	 * @param inputFileNameList 要打包的文件夹或文件的路径的列表(严重警告：inputFileNameList中的路径不能有targetFileName的祖先路径)
	 * @param targetFileName 打包后的文件路径
	 * @param base 压缩文件的路径
	 * @return
	 */
	public static File createTarGz(List<String> inputFileNameList, String targetFileName, String base) {
		TarArchiveOutputStream out = getTarOutputStream(targetFileName);
		File tarFile = new File(targetFileName);
		
		for (String inputFileName : inputFileNameList) {
			File inputFile = new File(inputFileName);
			tarPack(out, inputFile, base, tarFile);
		}

		try {
			if (null != out) {
				out.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		File tarGzFile = compress(tarFile);
		tarFile.delete();
		return tarGzFile;
	}

	private static void tarPack(TarArchiveOutputStream out, File inputFile, String base, File targetFile) {
		if (inputFile.isDirectory()) // 打包文件夹
		{
			packFolder(out, inputFile, base, targetFile);
		} else // 打包文件
		{
			packFile(out, inputFile, base);
		}
	}

	private static void packFolder(TarArchiveOutputStream out, File inputFile, String base, final File targetFile) {
		File[] fileList = inputFile.listFiles(new FileFilter() {
			@Override
			public boolean accept(File file) {
				return !file.equals(targetFile);
			}
		});
		base = base.length() == 0 ? "" : base + "/";
		try {
			if(!"".equals(base)){
				out.putArchiveEntry(new TarArchiveEntry(base));
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		for (File file : fileList) {
			tarPack(out, file, base + file.getName(), null);
		}
	}

	private static void packFile(TarArchiveOutputStream out, File inputFile, String base) {
		TarArchiveEntry entry = new TarArchiveEntry(base);

		entry.setSize(inputFile.length());
		try {
			out.putArchiveEntry(entry);
		} catch (IOException e) {
			e.printStackTrace();
		}
		FileInputStream in = null;
		try {
			in = new FileInputStream(inputFile);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		int b = 0;

		try {
			while ((b = in.read(B_ARRAY, 0, BUFFER)) != -1) {
				out.write(B_ARRAY, 0, b);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (null != in) {
					in.close();
				}
				if (null != out) {
					out.closeArchiveEntry();
				}
			} catch (IOException e) {

			}
		}
	}

	/**
	 * 把打包的tar文件压缩成gz格式
	 * @param srcFile 要压缩的tar文件路径
	 * @return
	 */
	private static File compress(File srcFile) {
		File target = new File(srcFile.getAbsolutePath() + ".gz");
		FileInputStream in = null;
		GZIPOutputStream out = null;
		try {
			in = new FileInputStream(srcFile);
			out = new GZIPOutputStream(new FileOutputStream(target));
			int number = 0;
			while ((number = in.read(B_ARRAY, 0, BUFFER)) != -1) {
				out.write(B_ARRAY, 0, number);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (in != null) {
					in.close();
				}

				if (out != null) {
					out.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return target;
	}

	private static TarArchiveOutputStream getTarOutputStream(String targetFileName) {
		// 如果打包文件没有.tar后缀名，就自动加上
		targetFileName = targetFileName.endsWith(".tar") ? targetFileName
				: targetFileName + ".tar";
		FileOutputStream fileOutputStream = null;
		try {
			fileOutputStream = new FileOutputStream(targetFileName);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(
				fileOutputStream);
		TarArchiveOutputStream out = new TarArchiveOutputStream(bufferedOutputStream, System.getProperty("file.encoding"));

		// 如果不加下面这段，当压缩包中的路径字节数超过100 byte时，就会报错
		out.setLongFileMode(TarOutputStream.LONGFILE_GNU);
		return out;
	}
	
	public static void main(String[] args) {
		createTarGz("E:/var", "E:/var/test5.tar","");
		
	}
	
}